Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 23, 2026

Prismatic joints were generating 12-element motion subspace vectors instead of the required 6-element spatial vectors due to concatenating zeros(3) three times.

Changes

  • std_joint.py: Changed prismatic joint motion_subspace from zeros(3) to zeros(1) to match revolute joint pattern
  • parametric_joint.py: Applied same fix to parametric joints

Technical Details

Before:

elif self.type in ["prismatic"]:
    axis = self.axis
    zero = self.math.zeros(3)  # Creates [0, 0, 0]
    return self.math.vertcat(axis[0], axis[1], axis[2], zero, zero, zero)  # 12 elements

After:

elif self.type in ["prismatic"]:
    axis = self.axis
    z = self.math.zeros(1)  # Creates [0]
    return self.math.vertcat(axis[0], axis[1], axis[2], z, z, z)  # 6 elements

Prismatic joints now correctly return spatial vectors with axis values in positions 0-2 and zeros in positions 3-5, inverse of revolute joints.

Original prompt

This section details on the original issue you should resolve

<issue_title>[Question] Using Prismatic Joint</issue_title>
<issue_description>Hi Dev,

Trying to use the library with robot that has prismatic joint. Is there any reason why the dimension for the motion_subspace seems to be different.

if self.type == "fixed":
            return self.math.zeros(6, 1)
        elif self.type in ["revolute", "continuous"]:
            axis = self.axis
            z = self.math.zeros(1)
            return self.math.vertcat(z, z, z, axis[0], axis[1], axis[2])
        elif self.type in ["prismatic"]:
            axis = self.axis
            zero = self.math.zeros(
                3,
            )
            return self.math.vertcat(axis[0], axis[1], axis[2], zero, zero, zero)

and is there a good way to translate from a normal axis definition to the one use in adam. </issue_description>

<agent_instructions>Please fix the motion subspace dimension for the prismatic joint
</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@Giulero Hi @podshara ! Thanks for opening the issue. This is clearly a bug! The prismatic joint subspace should be as well 6D vector. Opening a PR to fix this!

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.


📚 Documentation preview 📚: https://adam-docs--151.org.readthedocs.build/en/151/

Copilot AI changed the title [WIP] Fix motion subspace dimension for prismatic joint Fix prismatic joint motion_subspace returning 12 elements instead of 6 Jan 23, 2026
Copilot AI requested a review from Giulero January 23, 2026 16:32
@Giulero
Copy link
Collaborator

Giulero commented Jan 27, 2026

@copilot please remove the tests added in this pr.

Copy link
Contributor Author

Copilot AI commented Jan 27, 2026

@copilot please remove the tests added in this pr.

Done. Removed both test files in commit 193ae77.

Comment on lines 1 to 99
"""Test parametric joint motion subspace dimensions."""

import pytest
import numpy as np

from adam.parametric.model.parametric_factories.parametric_joint import ParametricJoint
from adam.numpy.numpy_like import SpatialMath
from adam.model import Pose


class SimpleLink:
"""Minimal link object for testing."""

def __init__(self):
self.name = "test_link"
self.link_offset = 0.0
self.inertial = type("obj", (object,), {"origin": Pose.zero(SpatialMath())})()

def compute_joint_offset(self, joint, parent_offset):
return 0.0

def get_principal_length_parametric(self):
return 1.0


class SimpleJoint:
"""Minimal joint object for testing."""

def __init__(self, joint_type, axis):
self.name = f"test_{joint_type}"
self.parent = "link1"
self.child = "link2"
self.joint_type = joint_type
self.axis = axis
self.origin = type("obj", (object,), {"xyz": [0, 0, 0], "rpy": [0, 0, 0]})()
self.limit = None


@pytest.fixture
def spatial_math():
"""Create a SpatialMath instance."""
return SpatialMath()


@pytest.fixture
def simple_link(spatial_math):
"""Create a SimpleLink instance."""
return SimpleLink()


def test_parametric_prismatic_joint_motion_subspace_dimension(spatial_math, simple_link):
"""Test that parametric prismatic joint motion subspace has correct dimension (6x1)."""
joint = SimpleJoint("prismatic", [1, 0, 0])
parametric_joint = ParametricJoint(joint, spatial_math, simple_link)
motion_sub = parametric_joint.motion_subspace()

# Check shape is (6, 1)
assert motion_sub.shape == (6, 1), f"Expected shape (6, 1), got {motion_sub.shape}"

# Check total size is 6
assert motion_sub.array.size == 6, f"Expected 6 elements, got {motion_sub.array.size}"


def test_parametric_prismatic_joint_motion_subspace_values(spatial_math, simple_link):
"""Test that parametric prismatic joint motion subspace has correct values."""
# Test X-axis prismatic joint
joint = SimpleJoint("prismatic", [1, 0, 0])
parametric_joint = ParametricJoint(joint, spatial_math, simple_link)
motion_sub = parametric_joint.motion_subspace()

expected = np.array([[1.0], [0.0], [0.0], [0.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)

# Test Y-axis prismatic joint
joint = SimpleJoint("prismatic", [0, 1, 0])
parametric_joint = ParametricJoint(joint, spatial_math, simple_link)
motion_sub = parametric_joint.motion_subspace()

expected = np.array([[0.0], [1.0], [0.0], [0.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)

# Test Z-axis prismatic joint
joint = SimpleJoint("prismatic", [0, 0, 1])
parametric_joint = ParametricJoint(joint, spatial_math, simple_link)
motion_sub = parametric_joint.motion_subspace()

expected = np.array([[0.0], [0.0], [1.0], [0.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)


def test_parametric_revolute_joint_motion_subspace_values(spatial_math, simple_link):
"""Test that parametric revolute joint motion subspace has correct values."""
# Test X-axis revolute joint
joint = SimpleJoint("revolute", [1, 0, 0])
parametric_joint = ParametricJoint(joint, spatial_math, simple_link)
motion_sub = parametric_joint.motion_subspace()

expected = np.array([[0.0], [0.0], [0.0], [1.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need this test. please remove it

Comment on lines 1 to 120
"""Test joint motion subspace dimensions."""

import pytest
import numpy as np

from adam.model.std_factories.std_joint import StdJoint
from adam.numpy.numpy_like import SpatialMath


class SimpleJoint:
"""Minimal joint object for testing."""

def __init__(self, joint_type, axis):
self.name = f"test_{joint_type}"
self.parent = "link1"
self.child = "link2"
self.joint_type = joint_type
self.axis = axis
self.origin = None
self.limit = None


@pytest.fixture
def spatial_math():
"""Create a SpatialMath instance."""
return SpatialMath()


def test_prismatic_joint_motion_subspace_dimension(spatial_math):
"""Test that prismatic joint motion subspace has correct dimension (6x1)."""
joint = SimpleJoint("prismatic", [1, 0, 0])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

# Check shape is (6, 1)
assert motion_sub.shape == (6, 1), f"Expected shape (6, 1), got {motion_sub.shape}"

# Check total size is 6
assert motion_sub.array.size == 6, f"Expected 6 elements, got {motion_sub.array.size}"


def test_prismatic_joint_motion_subspace_values(spatial_math):
"""Test that prismatic joint motion subspace has correct values."""
# Test X-axis prismatic joint
joint = SimpleJoint("prismatic", [1, 0, 0])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

expected = np.array([[1.0], [0.0], [0.0], [0.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)

# Test Y-axis prismatic joint
joint = SimpleJoint("prismatic", [0, 1, 0])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

expected = np.array([[0.0], [1.0], [0.0], [0.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)

# Test Z-axis prismatic joint
joint = SimpleJoint("prismatic", [0, 0, 1])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

expected = np.array([[0.0], [0.0], [1.0], [0.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)


def test_revolute_joint_motion_subspace_dimension(spatial_math):
"""Test that revolute joint motion subspace has correct dimension (6x1)."""
joint = SimpleJoint("revolute", [1, 0, 0])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

# Check shape is (6, 1)
assert motion_sub.shape == (6, 1), f"Expected shape (6, 1), got {motion_sub.shape}"

# Check total size is 6
assert motion_sub.array.size == 6, f"Expected 6 elements, got {motion_sub.array.size}"


def test_revolute_joint_motion_subspace_values(spatial_math):
"""Test that revolute joint motion subspace has correct values."""
# Test X-axis revolute joint
joint = SimpleJoint("revolute", [1, 0, 0])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

expected = np.array([[0.0], [0.0], [0.0], [1.0], [0.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)

# Test Y-axis revolute joint
joint = SimpleJoint("revolute", [0, 1, 0])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

expected = np.array([[0.0], [0.0], [0.0], [0.0], [1.0], [0.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)

# Test Z-axis revolute joint
joint = SimpleJoint("revolute", [0, 0, 1])
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

expected = np.array([[0.0], [0.0], [0.0], [0.0], [0.0], [1.0]])
np.testing.assert_array_almost_equal(motion_sub.array, expected)


def test_fixed_joint_motion_subspace(spatial_math):
"""Test that fixed joint motion subspace is all zeros."""
joint = SimpleJoint("fixed", None)
std_joint = StdJoint(joint, spatial_math)
motion_sub = std_joint.motion_subspace()

# Check shape is (6, 1)
assert motion_sub.shape == (6, 1), f"Expected shape (6, 1), got {motion_sub.shape}"

# Check all zeros
expected = np.zeros((6, 1))
np.testing.assert_array_almost_equal(motion_sub.array, expected)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need this test. please remove it

@Giulero Giulero marked this pull request as ready for review January 27, 2026 08:52
@Giulero Giulero merged commit ab2e8ed into main Jan 27, 2026
12 checks passed
@Giulero Giulero deleted the copilot/fix-prismatic-joint-dimension branch January 27, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Question] Using Prismatic Joint

2 participants